<?php

namespace common\service;

use common\exception\LogicException;
use common\model\EntityModel;
use common\model\PlanModel;
use common\model\ProjectModel;
use common\model\StepCategoryModel;
use common\model\StepModel;
use common\model\StepWorkflowModel;
use common\model\TaskModel;
use common\model\TaskRelationModel;
use support\ErrorCode;
use support\SingletonTrait;
use think\Exception;
use think\module\Module;
use Throwable;
use Webman\Stomp\Client;

class TaskGenerateService
{

    // 单例工具
    use SingletonTrait;

    public $assetModuleIdList = [];

    public function __construct()
    {
        $this->assetModuleIdList = [
            module_by_code('asset')['id'],
            module_by_code('design')['id'],
            module_by_code('level')['id'],
        ];
    }

    /**
     * 匹配并设置工序流
     * @param array $entities 要设置工序流的实体
     * @param int $projectId 项目id
     * @param int $moduleId 模块id
     * @return array
     * @throws \Exception
     */
    private function matchAndSetWorkflow(array $entities, int $projectId, int $moduleId)
    {
        $stepCategory = model(StepCategoryModel::class)->where(['entity_id' => $moduleId, 'parent_id' => 0])->cache(C('database.default_query_cache_time'))->find();
        if (empty($stepCategory)) {
            return $entities;
        }
        $stepWorkflow = model(StepWorkflowModel::class)
            ->where(['code' => $stepCategory['code'], 'project_id' => ['IN', [0, $projectId]], 'is_enable' => 'yes'])
            ->order('project_id desc')
            ->find();
        if (empty($stepWorkflow)) {
            return $entities;
        }
        $updateEntityIdList = [];
        foreach ($entities as &$entity) {
            if ($entity['step_workflow_id'] > 0) {
                continue;
            }
            $updateEntityIdList[] = $entity['id'];
            $entity['step_workflow_id'] = $stepWorkflow['id'];
        }
        if (empty($updateEntityIdList)) {
            return $entities;
        }
        model(EntityModel::class)->where(['id' => ['IN', implode(',', $updateEntityIdList)]])->save(['step_workflow_id' => $stepWorkflow['id']]);
        return $entities;
    }

    /**
     * 生成指定项目的实体任务
     * @param int $projectId
     * @param string $moduleCode
     * @param $entityIds
     * @param int $tenantId
     * @param string $entityStatus
     * @param string $taskDefaultStatus
     * @return array|array[]
     * @throws Exception
     * @throws Throwable
     */
    public function doGenerateEntityStepCategoryTask(int $projectId, string $moduleCode, $entityIds, int $tenantId, string $entityStatus = 'task_operation_to_be_generated', string $taskDefaultStatus = '', $stepCategoryTaskDefaultValueMap = [])
    {
        $entityModel = model(EntityModel::class);
        $taskModel = model(TaskModel::class);
        $module = module_by_code($moduleCode);
        $statusField = $moduleCode . '_status';
        $resData = [
            'success' => [],
            'error' => [],
            'added' => [],
        ];
        $entityWhere = [
            'project_id' => $projectId,
            'module_id' => $module['id'],
        ];

        if (!empty($entityIds)) {
            $entityWhere[] = ['id' => ['IN', $entityIds]];
        }
        $publishedStatusList = StatusService::getInstance()->getEntityPublishedStatusList($moduleCode);
        $publishedStatusIdList = array_column($publishedStatusList, 'id');
        // 获取还未分配的实体
        // @update 可修改实体属性重复生成任务
        $entityList = $entityModel
            ->field("id,name,code,project_id,module_id,step_workflow_id,initial_episode_code,showings_number,json,hour_proportion,is_append,json->>'$.$statusField' as status")
            ->where($entityWhere)
            ->select();
        if (empty($entityList)) {
            return $resData;
        }
        $entityList = array_column($entityList, null, 'id');
        $project = model(ProjectModel::class)->where(['id' => $projectId])->cache(C('database.default_query_cache_time'))->find();
        // 匹配工序流
        $entityList = $this->matchAndSetWorkflow($entityList, $project['id'], $module['id']);
        $generateTaskStatus = StatusService::getInstance()->getOne(['code' => $entityStatus], '*');

        // 找到当前实体绑定
        $stepWorkflowIds = [];
        // 查找当前实体初始集id
        $episodeCodeList = [];
        $entityIdList = [];
        foreach ($entityList as $key => $item) {

            if (empty($item['initial_episode_code'])) {
                //初始集检查
                $resData['error'][] = [
                    "id" => $item['id'],
                    "name" => $item['name'],
                    'message' => "don`t have initial_episode_code",
                ];
                unset($entityList[$key]);
                continue;
            }
            if (!in_array($item['initial_episode_code'], $episodeCodeList)) {
                $episodeCodeList[] = $item['initial_episode_code'];
            }
            if ((int)$item['step_workflow_id'] <= 0) {
                //工序流绑定检查
                $resData['error'][] = [
                    "id" => $item['id'],
                    "name" => $item['name'],
                    'message' => "step workflow is not set",
                ];
                unset($entityList[$key]);
                continue;
            }

            if (!in_array($item['step_workflow_id'], $stepWorkflowIds)) {
                $stepWorkflowIds[] = $item['step_workflow_id'];
            }
            $jsonFields = json_decode($item['json'], true);
            unset($item['json']);
            $entityList[$key] = array_merge($item, $jsonFields);
            $entityIdList[] = $item['id'];
        }
        if (empty($stepWorkflowIds) || empty($entityList)) {
            return $resData;
        }
        $episodeList = [];
        if (!empty($episodeCodeList)) {
            $episodeList = EpisodeService::getInstance()->selectEpisodeMap(['project_id' => $projectId, 'module_id' => module_by_code('episode')['id'], 'code' => ['IN', implode(',', $episodeCodeList)]], 'id,code', 'code', 'id');
        }
        $stepWorkflowService = StepWorkflowService::getInstance();
        $stepCategoryDict = $stepWorkflowService->getStepCategoryDictByStepWorkflowId($stepWorkflowIds);
        if (empty($stepCategoryDict)) {
            throw new LogicException("The data is not bound to the step flow.", ErrorCode::DATA_NOT_BOUND_TO_THE_STEP_FLOW);
        }
        $stepNodesDict = $stepWorkflowService->getStepNodesDictByStepWorkflowId($stepWorkflowIds);

        //处理任务的默认状态 格式化任务的json字段
        $statusService = StatusService::getInstance();
        if (!empty($taskDefaultStatus)) {
            $taskDefaultStatusObj = $statusService->getOne(['code' => $taskDefaultStatus], 'id');
        } else {
            $taskDefaultStatusObj = $statusService->getTaskDefaultStatus();
        }

        $taskJsonData = [];
        if (!empty($taskDefaultStatusObj)) {
            $taskJsonData = [
                "task_status" => $taskDefaultStatusObj['id']
            ];
        }

        $entityTaskList = model(TaskModel::class)
            ->where([
                'tenant_id' => $tenantId,
                'project_id' => $projectId,
                'entity_id' => ['IN', implode(',', $entityIdList)]])
            ->field('id,entity_id,step_category_id,step_id,step_type,task.json')
            ->select();

        $generatedStepIdListGroupByEntityId = [];
        $generatedStepCategoryTaskGroupByEntityId = [];
        foreach ($entityTaskList as $entityTask) {
            $entityTaskJson = json_decode($entityTask['json'] ?? '{}', true);
            $entityTask['task_status'] = $entityTaskJson['task_status'] ?? null;
            if ($entityTask['step_id'] > 0) {
                $generatedStepIdListGroupByEntityId[$entityTask['entity_id']][$entityTask['step_id']] = $entityTask;
            } else if ($entityTask['step_type'] == TaskModel::STEP_TYPE_STEP_CATEGORY) {
                $generatedStepCategoryTaskGroupByEntityId[$entityTask['entity_id']][] = $entityTask;
            }
        }

        $closeStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        $doneStatus = StatusService::getInstance()->getTaskDoneStatus();
        $notStartedStatus = StatusService::getInstance()->getTaskDefaultStatus();
        $notStartedGroupStatus = StatusService::getInstance()->getNotStartedStatus();
        $notStartedIdGroup = array_column($notStartedGroupStatus, 'id');
        $categoryTaskAdded = [];
        $stepTaskAdded = [];
        $stepTaskCheckAssemble = [];
        $commonService = CommonService::instance('entity');
        $commonService->setCurrentModuleCode($moduleCode);
        $taskModel->startTrans();//强制开启事务
        try {
            foreach ($entityList as $entityItem) {
                $stepWorkflowId = $entityItem['step_workflow_id'];
                if (!isset($stepCategoryDict[$stepWorkflowId])) {
                    $resData['error'][] = [
                        "id" => $entityItem['id'],
                        "name" => $entityItem['name'],
                        'message' => "step workflow [{$stepWorkflowId}] is not exist",
                    ];
                    continue;
                }
                $episodeId = $episodeList[$entityItem['initial_episode_code']] ?? 0;
                $entityGeneratedStepIdList = $generatedStepIdListGroupByEntityId[$entityItem['id']] ?? [];
                $entityGeneratedStepCategoryTaskList = $generatedStepCategoryTaskGroupByEntityId[$entityItem['id']] ?? [];
                $entityGeneratedStepCategoryIdList = array_column($entityGeneratedStepCategoryTaskList, 'step_category_id');
                $entityGeneratedStepCategoryTaskList = array_column($entityGeneratedStepCategoryTaskList, null, 'step_category_id');
                // 当前实体分类任务InsertId map
                $stepCategoryTaskMap = [];
                $tmpCategoryTaskAdded = [];
                $orderStr = '';
                // 按照工序分类连接顺序生成任务
                foreach ($stepCategoryDict[$stepWorkflowId]['pre_and_post_relation'] as $stepWorkFlowItem) {
                    $fromStepCategoryId = $stepWorkFlowItem['from_step_category_id'];
                    $stepCategoryItem = $stepCategoryDict[$stepWorkflowId]['step_category_data'][$fromStepCategoryId];
                    if (in_array($stepCategoryItem['id'], $entityGeneratedStepCategoryIdList)) {
                        // 已经生成过任务了
                        $stepCategoryTaskMap[$stepCategoryItem['id']] = $entityGeneratedStepCategoryTaskList[$stepCategoryItem['id']]['id'];
                        continue;
                    }
                    // 前置分类任务id
                    $preTaskId = $stepCategoryTaskMap[$stepWorkFlowItem['pre_step_category_id']] ?? 0;

                    if (in_array($entityItem['module_id'], $this->assetModuleIdList)) {
                        $orderStr = $entityItem['code'];

                        //source_entity_id
                        if ($entityItem['module_id'] == module_by_code('design')['id']) {
                            $sourceEntityId = $entityItem['id'];
                        } else {
                            $currentModuleCode = module_by_id($entityItem['module_id'])['code'];
                            $sourceEntityId = $entityItem[$currentModuleCode . "_design"] ?? 0;
                        }

                    } else {
                        $orderStr = $entityItem['initial_episode_code'] . '/' . $entityItem['showings_number'] . '/' . $entityItem['code'];
                        if ($entityItem['module_id'] == module_by_code('storyboard')['id']) {
                            $sourceEntityId = $entityItem['id'];
                        } else {
                            $sourceEntityId = $entityItem["shot_storyboard"] ?? 0;
                        }
                    }

                    $stepCategoryTask = [
                        'name' => $stepCategoryItem['name'],
                        'code' => $stepCategoryItem['code'],
                        'type' => 'normal',
                        'project_id' => $entityItem['project_id'],
                        'entity_id' => $entityItem['id'],
                        'source_entity_id' => $sourceEntityId,
                        'entity_module_id' => $entityItem['module_id'],
                        'step_type' => 'step_category',
                        'step_category_id' => $stepCategoryItem['id'],
                        'plan_start_time' => 0,
                        'plan_end_time' => 0,
                        'created_by' => fill_created_by(),
                        'tenant_id' => $tenantId,
                        'created' => time(),
                        'uuid' => create_uuid(),
                        'json' => $taskJsonData,
                        'pre_task_id' => $preTaskId, // 前置任务id值
                        'entity_code' => $entityItem['code'],
                        'episode_id' => $episodeId,
                        'order_str' => $orderStr,
                        'standard_plan_time' => 0,
                        'standard_plan_id' => 0,
                        'manual_man_hour' => 0,
                    ];
                    if (isset($stepCategoryTaskDefaultValueMap[$episodeId][$stepCategoryItem['id']])) {
                        $stepCategoryTask = array_merge($stepCategoryTask, $stepCategoryTaskDefaultValueMap[$episodeId][$stepCategoryItem['id']]);
                    }
                    $stepCategoryTask['json']['task_is_append'] = $entityItem['is_append'];
                    $stepCategoryTask['json'] = json_encode($stepCategoryTask['json']);
                    if (isset($stepCategoryTask['standard_plan_time']) && empty($stepCategoryTask['standard_plan_time'])) {
                        $stepCategoryTask['standard_plan_time'] = 0;
                    }
                    $tmpCategoryTaskAdded[] = $stepCategoryTaskMap[$stepCategoryItem['id']] = (int)$taskModel->add($stepCategoryTask);
                }

                // 统计工序分类任务生成数量
                $categoryTaskAdded = array_merge($categoryTaskAdded, $tmpCategoryTaskAdded);
                if (empty($stepCategoryTaskMap)) {
                    $resData['error'][] = [
                        "id" => $entityItem['id'],
                        "name" => $entityItem['name'],
                        'message' => "did not generated task, may check your entity attribute",
                    ];
                    continue;
                }

                $needEnableDisableStepTaskList = [];

                /**
                 * 生成工序任务
                 * 已有任务，但不在工序流中的，设置废除
                 */
                $currentStepCategoryTasksAdded = model(TaskModel::class)->where(['id' => ['IN', implode(',', $stepCategoryTaskMap)]])->select();
                $tmpStepTaskAdded = [];
                $recoverStatusToNotStarted = [];
                $needDeprecateEntityGeneratedStepIdList = $entityGeneratedStepIdList;
                foreach ($currentStepCategoryTasksAdded as $stepCategoryTask) {
                    $workflowConfig = $stepNodesDict[$stepWorkflowId];
                    $generateStepTaskNodes = [];
                    foreach ($workflowConfig['nodes'] as $nodeList) {
                        $tmpGenerateNodes = [];
                        foreach ($nodeList as $node) {
                            // 是不是当前环节
                            if ($stepCategoryTask['step_category_id'] != $node['step_category_id']) {
                                continue;
                            }
                            APP_DEBUG && trace(var_export($node, true), 'DEBUG');
                            APP_DEBUG && trace(var_export($entityItem, true), 'DEBUG');
                            // 匹配是否要生成工序任务
                            $enableOrDisable = $stepWorkflowService->matchNodeRule($module['code'], $node, $entityItem);

                            // 已经生成过任务
                            if (array_key_exists($node['id'], $entityGeneratedStepIdList)) {
                                unset($needDeprecateEntityGeneratedStepIdList[$node['id']]);
                                // 判断是否需要启用或者废除
                                $taskStatus = $entityGeneratedStepIdList[$node['id']]['task_status'];
                                $taskDisabled = $taskStatus == $closeStatus['id'];
                                $entityGeneratedStepIdList[$node['id']]['task_disabled'] = $taskDisabled ? 'yes' : 'no';
                                if (
                                    $taskDisabled && $enableOrDisable
                                    || !$taskDisabled && !$enableOrDisable
                                ) {
                                    $needEnableDisableStepTaskList[] = $entityGeneratedStepIdList[$node['id']];
                                }
                                // 启用并且未开始的回复未开始
                                if ($enableOrDisable && in_array($taskStatus, $notStartedIdGroup)) {
                                    $recoverStatusToNotStarted[] = $entityGeneratedStepIdList[$node['id']];
                                }
                                // 启用并且已完成的检查下游组装状态
                                if ($enableOrDisable && $taskStatus == $doneStatus['id']) {
                                    $stepTaskCheckAssemble[] = $entityGeneratedStepIdList[$node['id']];
                                }
                                continue;
                            }
                            $node['enable'] = $enableOrDisable;
                            $tmpGenerateNodes[] = $node;
                        }
                        if (empty($tmpGenerateNodes)) {
                            continue;
                        }
                        $generateStepTaskNodes[] = $tmpGenerateNodes;
                    }
                    // 不需要执行生成工序任务
                    if (empty($generateStepTaskNodes)) {
                        continue;
                    }
                    $currentStepTaskAddRes = $this->generateStepTask($stepCategoryTask, $entityItem, $generateStepTaskNodes, $workflowConfig['edges'], $taskDefaultStatus);
                    $tmpStepTaskAdded = array_merge($tmpStepTaskAdded, $currentStepTaskAddRes);
                }
                foreach ($needDeprecateEntityGeneratedStepIdList as $item) {
                    // 废除其他的
                    $item['task_disabled'] = 'yes';
                    $needEnableDisableStepTaskList[] = $item;
                }
                // 处理任务的启用与废除
                if (!empty($needEnableDisableStepTaskList)) {
                    $needEnableDisableStepTaskList = array_group_by($needEnableDisableStepTaskList, 'task_disabled');
                    foreach ($needEnableDisableStepTaskList as $enableDisableStatus => $enableDisableStepTaskList) {
                        $updateTaskIds = implode(',', array_column($enableDisableStepTaskList, 'id'));
                        if ($enableDisableStatus === 'yes') {
                            $taskModel->execute("UPDATE task SET json = if(json ->> '$.task_deprecate_data' is null or json ->> '$.task_deprecate_data' = '',
              json_object('task_status', {$taskDefaultStatusObj['id']}), json ->> '$.task_deprecate_data') where id in ({$updateTaskIds})");
                        } else {
                            $taskModel->execute("UPDATE task SET json = json_set(json, '$.task_deprecate_data', json, '$.task_status', {$closeStatus['id']}) where id in ({$updateTaskIds})");
                        }
                    }
                }
                if (!empty($recoverStatusToNotStarted)) {
                    $recoverIdList = implode(',', array_column($recoverStatusToNotStarted, 'id'));
                    $taskModel->execute("UPDATE task SET json = json_set(json, '$.task_status', {$notStartedStatus['id']}) where id in ({$recoverIdList})");
                }

                // 更新实体状态
                $entityUpdateData = [
                    'id' => $entityItem['id'],
                    'is_generate' => 'yes',
                ];
                if (!empty($generateTaskStatus) && !in_array($entityItem['status'], $publishedStatusIdList)) {
                    $entityUpdateData[$statusField] = $generateTaskStatus['id'];
                }
                $commonService->update($entityUpdateData, $moduleCode, false);

                // 统计结果
                $stepTaskAdded = array_merge($stepTaskAdded, array_column($tmpStepTaskAdded, 'id'));
                $taskCount = count($tmpCategoryTaskAdded) + count($tmpStepTaskAdded);
                $resData['success'][] = [
                    "id" => $entityItem['id'],
                    "name" => $entityItem['name'],
                    'message' => "generated {$taskCount} rows task",
                ];
            }
            $taskModel->commit();
            $resData['added'] = array_merge($categoryTaskAdded, $stepTaskAdded);
            if (!empty($stepTaskCheckAssemble)) {
                foreach ($stepTaskCheckAssemble as $task) {
                    Client::send('task_downstream_assemble_check', build_queue_data_with_xu(['task_id' => $task['id']]));
                }
            }
            return $resData;
        } catch (Throwable $e) {
            $taskModel->rollback();
            throw $e;
        }
    }


    /**
     * 生成指定项目的实体任务
     * @param int $projectId
     * @param $entityIds
     * @param int $tenantId
     * @param string $entityStatus
     * @param array $stepCategoryTaskDefaultValueMap
     * @return array|array[]
     * @throws Exception
     * @throws Throwable
     */
    public function doGenerateEntityStepCategoryTaskV3(int $projectId, $entityIds, int $tenantId, string $entityStatus, array $stepCategoryTaskDefaultValueMap = []): array
    {
        $resData = [
            'success' => [],
            'error' => [],
            'added' => [],
        ];
        $entityWhere = [
            'entity.project_id' => $projectId
        ];

        if (!empty($entityIds)) {
            $entityWhere[] = ['entity.id' => ['IN', $entityIds]];
        }

        $entityFields = [
            "entity.id",
            "entity.name",
            "entity.code",
            "entity.project_id",
            "entity.module_id",
            "entity.step_workflow_id",
            "entity.initial_episode_code",
            "entity.showings_number",
            "entity.json",
            "entity.hour_proportion",
            "entity.is_append",
            "module.code as module_code",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', module.code, '_status'))) AS status"
        ];
        $entityList = model(EntityModel::class)
            ->join('module on module.id = entity.module_id')
            ->field($entityFields)
            ->where($entityWhere)
            ->select();

        if (empty($entityList)) {
            return $resData;
        }
        $entityList = array_column($entityList, null, 'id');

        $generateTaskStatus = StatusService::getInstance()->getOne(['code' => $entityStatus], '*');

        // 找到当前实体绑定
        $stepWorkflowIds = [];
        // 查找当前实体初始集id
        $episodeCodeList = [];
        $entityIdList = [];

        foreach ($entityList as $key => $item) {

            //初始集检查
            if (empty($item['initial_episode_code'])) {
                $resData['error'][] = [
                    "id" => $item['id'],
                    "name" => $item['name'],
                    'message' => "don`t have initial_episode_code",
                ];
                unset($entityList[$key]);
                continue;
            }
            if (!in_array($item['initial_episode_code'], $episodeCodeList)) {
                $episodeCodeList[] = $item['initial_episode_code'];
            }
            //工序流绑定检查
            if ((int)$item['step_workflow_id'] <= 0) {
                $resData['error'][] = [
                    "id" => $item['id'],
                    "name" => $item['name'],
                    'message' => "step workflow is not set",
                ];
                unset($entityList[$key]);
                continue;
            }
            if (!in_array($item['step_workflow_id'], $stepWorkflowIds)) {
                $stepWorkflowIds[] = $item['step_workflow_id'];
            }
            $jsonFields = json_decode($item['json'], true);
            unset($item['json']);
            $entityList[$key] = array_merge($item, $jsonFields);
            $entityIdList[] = $item['id'];
        }

        if (empty($stepWorkflowIds) || empty($entityList)) {
            return $resData;
        }

        $episodeList = [];
        if (!empty($episodeCodeList)) {
            $episodeList = EpisodeService::getInstance()->selectEpisodeMap(['project_id' => $projectId, 'module_id' => module_by_code('episode')['id'], 'code' => ['IN', implode(',', $episodeCodeList)]], 'id,code', 'code', 'id');
        }

        $stepWorkflowService = StepWorkflowService::getInstance();
        $stepCategoryDict = $stepWorkflowService->getStepCategoryDictByStepWorkflowId($stepWorkflowIds);
        if (empty($stepCategoryDict)) {
            throw new LogicException("The data is not bound to the step flow.", ErrorCode::DATA_NOT_BOUND_TO_THE_STEP_FLOW);
        }
        $stepNodesDict = $stepWorkflowService->getStepNodesDictByStepWorkflowId($stepWorkflowIds, true);

        //处理任务的默认状态 格式化任务的json字段
        $statusService = StatusService::getInstance();
        $taskDefaultStatusObj = $statusService->getTaskDefaultStatus();

        $taskJsonData = [];
        if (!empty($taskDefaultStatusObj)) {
            $taskJsonData = [
                "task_status" => $taskDefaultStatusObj['id']
            ];
        }

        $entityTaskList = model(TaskModel::class)
            ->where([
                'tenant_id' => $tenantId,
                'project_id' => $projectId,
                'entity_id' => ['IN', implode(',', $entityIdList)]])
            ->field('id,entity_id,step_category_id,step_id,step_type,task.json')
            ->select();

        $generatedStepIdListGroupByEntityId = [];
        $generatedStepCategoryTaskGroupByEntityId = [];
        foreach ($entityTaskList as $entityTask) {
            $entityTaskJson = json_decode($entityTask['json'] ?? '{}', true);
            $entityTask['task_status'] = $entityTaskJson['task_status'] ?? null;
            if ($entityTask['step_id'] > 0) {
                $generatedStepIdListGroupByEntityId[$entityTask['entity_id']][$entityTask['step_id']] = $entityTask;
            } else if ($entityTask['step_type'] == TaskModel::STEP_TYPE_STEP_CATEGORY) {
                $generatedStepCategoryTaskGroupByEntityId[$entityTask['entity_id']][] = $entityTask;
            }
        }

        $closeStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        $categoryTaskAdded = [];
        $stepTaskAdded = [];

        $commonService = CommonService::instance('entity');

        // 创建任务
        $taskModel = model(TaskModel::class);
        $taskModel->startTrans();//强制开启事务
        try {
            foreach ($entityList as $entityItem) {
                $stepWorkflowId = $entityItem['step_workflow_id'] ?? 0;
                if (!isset($stepCategoryDict[$stepWorkflowId])) {
                    $resData['error'][] = [
                        "id" => $entityItem['id'],
                        "name" => $entityItem['name'],
                        'message' => "step workflow [{$stepWorkflowId}] is not exist",
                    ];
                    continue;
                }
                $episodeId = $episodeList[$entityItem['initial_episode_code']] ?? 0;
                $entityGeneratedStepIdList = $generatedStepIdListGroupByEntityId[$entityItem['id']] ?? [];
                $entityGeneratedStepCategoryTaskList = $generatedStepCategoryTaskGroupByEntityId[$entityItem['id']] ?? [];
                $entityGeneratedStepCategoryIdList = array_column($entityGeneratedStepCategoryTaskList, 'step_category_id');
                $entityGeneratedStepCategoryTaskList = array_column($entityGeneratedStepCategoryTaskList, null, 'step_category_id');
                // 当前实体分类任务InsertId map
                $stepCategoryTaskMap = [];
                $tmpCategoryTaskAdded = [];
                // 按照工序分类连接顺序生成任务
                foreach ($stepCategoryDict[$stepWorkflowId]['pre_and_post_relation'] as $stepWorkFlowItem) {
                    $stepCategoryItem = $stepCategoryDict[$stepWorkflowId]['step_category_data'][$stepWorkFlowItem['from_step_category_id']];

                    if (in_array($stepCategoryItem['id'], $entityGeneratedStepCategoryIdList)) {
                        // 已经生成过任务了
                        $stepCategoryTaskMap[$stepCategoryItem['id']] = $entityGeneratedStepCategoryTaskList[$stepCategoryItem['id']]['id'];
                        continue;
                    }
                    // 前置分类任务id
                    $preTaskId = $stepCategoryTaskMap[$stepWorkFlowItem['pre_step_category_id']] ?? 0;

                    if (in_array($entityItem['module_id'], $this->assetModuleIdList)) {
                        $orderStr = $entityItem['code'];
                    } else {
                        $orderStr = $entityItem['initial_episode_code'] . '/' . $entityItem['showings_number'] . '/' . $entityItem['code'];
                    }
                    $taskJsonData['step_workflow_id'] = $stepWorkflowId;

                    $stepCategoryTask = [
                        'name' => $stepCategoryItem['name'],
                        'code' => $stepCategoryItem['code'],
                        'type' => 'normal',
                        'project_id' => $entityItem['project_id'],
                        'entity_id' => $entityItem['id'],
                        'source_entity_id' => $entityItem['id'],
                        'entity_module_id' => $entityItem['module_id'],
                        'step_type' => 'step_category',
                        'step_category_id' => $stepCategoryItem['id'],
                        'plan_start_time' => 0,
                        'plan_end_time' => 0,
                        'created_by' => fill_created_by(),
                        'tenant_id' => $tenantId,
                        'created' => time(),
                        'uuid' => create_uuid(),
                        'json' => $taskJsonData,
                        'pre_task_id' => $preTaskId, // 前置任务id值
                        'entity_code' => $entityItem['code'],
                        'episode_id' => $episodeId,
                        'order_str' => $orderStr,
                        'standard_plan_time' => 0,
                        'standard_plan_id' => 0,
                        'manual_man_hour' => 0,
                    ];
                    if (isset($stepCategoryTaskDefaultValueMap[$episodeId][$stepCategoryItem['id']])) {
                        $stepCategoryTask = array_merge($stepCategoryTask, $stepCategoryTaskDefaultValueMap[$episodeId][$stepCategoryItem['id']]);
                    }
                    $stepCategoryTask['json']['task_is_append'] = $entityItem['is_append'];
                    $stepCategoryTask['json'] = json_encode($stepCategoryTask['json']);
                    if (isset($stepCategoryTask['standard_plan_time']) && empty($stepCategoryTask['standard_plan_time'])) {
                        $stepCategoryTask['standard_plan_time'] = 0;
                    }
                    $tmpCategoryTaskAdded[] = $stepCategoryTaskMap[$stepCategoryItem['id']] = (int)$taskModel->add($stepCategoryTask);
                }

                // 统计工序分类任务生成数量
                $categoryTaskAdded = array_merge($categoryTaskAdded, $tmpCategoryTaskAdded);
                if (empty($stepCategoryTaskMap)) {
                    $resData['error'][] = [
                        "id" => $entityItem['id'],
                        "name" => $entityItem['name'],
                        'message' => "did not generated task, may check your entity attribute",
                    ];
                    continue;
                }

                $needEnableDisableStepTaskList = [];

                /**
                 * 生成工序任务
                 */
                $currentStepCategoryTasksAdded = model(TaskModel::class)->where(['id' => ['IN', implode(',', $stepCategoryTaskMap)]])->select();
                $tmpStepTaskAdded = [];
                foreach ($currentStepCategoryTasksAdded as $stepCategoryTask) {
                    $workflowConfig = $stepNodesDict[$stepWorkflowId];
                    $generateStepTaskNodes = [];
                    foreach ($workflowConfig['nodes'] as $nodeList) {
                        $tmpGenerateNodes = [];
                        foreach ($nodeList as $node) {
                            // 是不是当前环节
                            if ($stepCategoryTask['step_category_id'] != $node['step_category_id']) {
                                continue;
                            }
                            APP_DEBUG && trace(var_export($node, true), 'DEBUG');
                            APP_DEBUG && trace(var_export($entityItem, true), 'DEBUG');
                            // 匹配是否要生成工序任务
                            $enableOrDisable = $stepWorkflowService->matchNodeRule($entityItem['module_code'], $node, $entityItem);

                            // 已经生成过任务
                            if (array_key_exists($node['id'], $entityGeneratedStepIdList)) {
                                // 判断是否需要启用或者废除
                                $taskStatus = $entityGeneratedStepIdList[$node['id']]['task_status'];
                                $taskDisabled = $taskStatus == $closeStatus['id'];
                                $entityGeneratedStepIdList[$node['id']]['task_disabled'] = $taskDisabled ? 'yes' : 'no';
                                if (
                                    $taskDisabled && $enableOrDisable
                                    || !$taskDisabled && !$enableOrDisable
                                ) {
                                    $needEnableDisableStepTaskList[] = $entityGeneratedStepIdList[$node['id']];
                                }
                                continue;
                            }
                            $node['enable'] = $enableOrDisable;
                            $tmpGenerateNodes[] = $node;
                        }
                        if (empty($tmpGenerateNodes)) {
                            continue;
                        }
                        $generateStepTaskNodes[] = $tmpGenerateNodes;
                    }
                    // 不需要执行生成工序任务
                    if (empty($generateStepTaskNodes)) {
                        continue;
                    }
                    $currentStepTaskAddRes = $this->generateStepTask($stepCategoryTask, $entityItem, $generateStepTaskNodes, $workflowConfig['edges']);
                    $tmpStepTaskAdded = array_merge($tmpStepTaskAdded, $currentStepTaskAddRes);
                }


                // 处理任务的启用与废除
                if (!empty($needEnableDisableStepTaskList)) {
                    $needEnableDisableStepTaskList = array_group_by($needEnableDisableStepTaskList, 'task_disabled');
                    foreach ($needEnableDisableStepTaskList as $enableDisableStatus => $enableDisableStepTaskList) {
                        $updateTaskIds = implode(',', array_column($enableDisableStepTaskList, 'id'));
                        if ($enableDisableStatus === 'yes') {
                            $taskModel->execute("UPDATE task SET json = if(json ->> '$.task_deprecate_data' is null or json ->> '$.task_deprecate_data' = '',
              json_object('task_status', {$taskDefaultStatusObj['id']}), json ->> '$.task_deprecate_data') where id in ({$updateTaskIds})");
                        } else {
                            $taskModel->execute("UPDATE task SET json = json_set(json, '$.task_deprecate_data', json, '$.task_status', {$closeStatus['id']}) where id in ({$updateTaskIds})");
                        }
                    }
                }

                // 更新实体状态
                $entityUpdateData = [
                    'id' => $entityItem['id'],
                    'is_generate' => 'yes',
                ];
                $publishedStatusList = StatusService::getInstance()->getEntityPublishedStatusList($entityItem['module_code']);
                $publishedStatusIdList = array_column($publishedStatusList, 'id');
                if (!empty($generateTaskStatus) && !in_array($entityItem['status'], $publishedStatusIdList)) {
                    $entityUpdateData[$entityItem['status']] = $generateTaskStatus['id'];
                }
                $commonService->update($entityUpdateData, $entityItem['module_code'], false);

                // 统计结果
                $stepTaskAdded = array_merge($stepTaskAdded, array_column($tmpStepTaskAdded, 'id'));
                $taskCount = count($tmpCategoryTaskAdded) + count($tmpStepTaskAdded);
                $resData['success'][] = [
                    "id" => $entityItem['id'],
                    "name" => $entityItem['name'],
                    'message' => "generated {$taskCount} rows task",
                ];
            }
            $taskModel->commit();
            $resData['added'] = array_merge($categoryTaskAdded, $stepTaskAdded);
            return $resData;
        } catch (Throwable $e) {
            $taskModel->rollback();
            throw $e;
        }
    }


    /**
     * 克隆关联的实体
     * @param int $entityId 实体id
     * @param int $projectId 项目id
     * @param int $tenantId 租户id
     * @param $sourceProjectId
     * @return int
     * @throws \Exception
     */
    public function cloneRelatedEntity($entityId, $projectId, $tenantId, $sourceProjectId)
    {
        $parentEntity = model(EntityModel::class)->where(['id' => $entityId])->find();
        if (!empty($parentEntity)) {
            /**
             * 处理初始集
             * 集在一个项目里 靠code唯一 没有关联的上级数据 不需要判断其他条件
             */
            if (!empty($parentEntity['initial_episode_code'])) {
                $episode = model(EntityModel::class)->where([
                    'code' => $parentEntity['initial_episode_code'],
                    'project_id' => $projectId,
                    'tenant_id' => $tenantId,
                    'module_id' => Module::$moduleDictData['module_index_by_code']['episode']['id']
                ])->find();
                if (empty($episode)) {
                    $episode = model(EntityModel::class)->where([
                        'code' => $parentEntity['initial_episode_code'],
                        'project_id' => $parentEntity['project_id'],
                        'tenant_id' => $parentEntity['tenant_id'],
                        'module_id' => Module::$moduleDictData['module_index_by_code']['episode']['id'],
                    ])->find();
                    $newEpisode = $episode;
                    $newEpisode['tenant_id'] = $tenantId;
                    $newEpisode['project_id'] = $projectId;
                    $newEpisode['uuid'] = create_uuid();
                    unset($newEpisode['id']);
                    $episodeId = model(EntityModel::class)->add($newEpisode);
                    trace("copy episode", "DEBUG");
                } else {
                    $episodeId = $episode['id'];
                }

                // 没有集计划 克隆集计划
                if (empty(model(PlanModel::class)->where(['entity_id' => $episodeId, 'type' => 'episode'])->find())) {
                    $oldEpisode = model(EntityModel::class)->where(['project_id' => $sourceProjectId, 'module_id' => Module::$moduleDictData['module_index_by_code']['episode']['id'], 'code' => $parentEntity['initial_episode_code']])->find();
                    if (!empty($oldEpisode)) {
                        $oldPlan = model(PlanModel::class)->where(['entity_id' => $oldEpisode['id'], 'type' => 'episode'])->find();
                        if (!empty($oldPlan)) {
                            unset($oldPlan['id']);
                            $oldPlan['project_id'] = $projectId;
                            $oldPlan['tenant_id'] = $tenantId;
                            $oldPlan['entity_id'] = $episodeId;
                            model(PlanModel::class)->add($oldPlan);
                        }
                    }
                }
            }
            /**
             *  判断实体是否存在
             *  存在就不进行实体克隆处理
             *  不存在就递归处理实体关系
             */
            if ($parentEntity['entity_id']) {
                /**
                 * 父级实体id不为0 则递归处理上级实体 等待返回上级的实体id
                 */
                $newEntity = $parentEntity;
                unset($newEntity['id']);
                $parentEntity['entity_id'] = $this->cloneRelatedEntity($parentEntity['entity_id'], $projectId, $tenantId, $sourceProjectId);
            }
            // 克隆当前实体
            // 更具当前实体的类型 判断字段唯一性

            $moduleUniqueValidationFields = model(EntityModel::class)->getModuleUniqueValidation(module_by_id($parentEntity['module_id'])['code']);
            $where = [];
            foreach ($moduleUniqueValidationFields as $field) {
                if ($field === 'tenant_id') {
                    $where['tenant_id'] = $tenantId;
                } else if ($field === 'project_id') {
                    $where['project_id'] = $projectId;
                } else {
                    $where[$field] = $parentEntity[$field];
                }
            }
            $newEntity = model(EntityModel::class)->where($where)->find();
            if (empty($newEntity)) {
                $newEntity = $parentEntity;
                unset($newEntity['id']);
                $newEntity['tenant_id'] = $tenantId;
                $newEntity['project_id'] = $projectId;
                $newEntity['uuid'] = create_uuid();
                $newEntityId = model(EntityModel::class)->add($newEntity);
                trace("copy entity", "DEBUG");
            } else {
                $newEntityId = $newEntity['id'];
            }

            return $newEntityId;
        }
        return 0;

    }

    /**
     * 复制一个任务到指定项目的租户下
     * 需要复制任务的实体 任务关联的集
     * @param $task
     * @param $projectId
     * @param $tenantId
     * @param $taskType
     * @return array|mixed|string|null
     * @throws \Exception
     */
    public function copyTask($task, $projectId, $tenantId, $taskType = "normal")
    {
        $taskModel = model(TaskModel::class);
        $shadowData = $task;
        // 如果克隆的是影子任务  那么需要克隆关联的实体 和 初始集
        if ($taskType == "shadow") {
            APP_DEBUG && trace("before copy related entity", "DEBUG");
            $shadowData['entity_id'] = $this->cloneRelatedEntity($shadowData['entity_id'], $projectId, $tenantId, $task['project_id']);
            APP_DEBUG && trace("after copy related entity", "DEBUG");

            if ($shadowData['episode_id'] > 0) {
                $shadowData['episode_id'] = $this->copyEpisode($shadowData['episode_id'], $projectId, $tenantId);
            }
        }


        unset($shadowData['id'], $shadowData['created'], $shadowData['executor_type']);
        $shadowData['tenant_id'] = $tenantId;
        $shadowData['project_id'] = $projectId;
        $shadowData['type'] = $taskType;
        $shadowData['created'] = time();
        /**
         * 不使用addItem 的特性
         * 因为 addItem 的自动完成会影响租户id的写入
         */

        $shadowTask = $taskModel->add($shadowData);
        if ($shadowTask === false) {
            throw new LogicException('clone task failure' . $taskModel->getError(), ErrorCode::TASK_CLONE_FAILURE);
        }
        $shadowTask = $taskModel->find($shadowTask);
        $relationData = [
            'source_task_id' => $task['id'],
            'target_task_id' => $shadowTask['id'],
            'relation_type' => $taskType,
        ];
        $projectId = $task['project_id'] ?? 0;
        model(TaskRelationModel::class)->setSuffix(sprintf('_project_%d', $projectId))->addItem($relationData);
        return $shadowTask;
    }


    /**
     * 生成当前step_category类型task 下面的step工序任务task
     * @param $task
     * @param $entity
     * @param $steps
     * @param $edges
     * @param string $taskDefaultStatus
     * @return array
     * @throws \Exception
     */
    public function generateStepTask($task, $entity, $steps, $edges, string $taskDefaultStatus = '')
    {
        if (empty($steps)) {
            return [];
        }
        $task['step_type'] = 'step';
        $task['executor'] = 0;
        unset($task['created']);
        $taskJson = json_decode($task['json'], true);
        $taskJson = $taskJson ?? [];
        if (empty($taskDefaultStatus)) {
            $taskDefaultStatus = StatusService::getInstance()->getTaskDefaultStatus();
        } else {
            $taskDefaultStatus = StatusService::getInstance()->getOne(['code' => $taskDefaultStatus], 'id');
        }
        $taskCloseStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();

        if (!empty($taskDefaultStatus)) {
            $taskJson['task_status'] = $taskDefaultStatus['id'];
        }
        $taskJson['task_workload_remain'] = 100;
        $taskJson['task_is_append'] = $entity['is_append'] ?? 'no';
        $taskJson['step_workflow_id'] = $entity['step_workflow_id'] ?? 0;
        $stepTaskMap = [];
        foreach ($steps as $stepList) {
            //遍历甬道里的工序
            foreach ($stepList as $step) {
                // 使用实体工时比例转换manHour
                if ($entity['hour_proportion'] > 0 && isset($step['man_hour'])) {
                    $step['man_hour'] = $step['man_hour'] * $entity['hour_proportion'];
                }
                $task['json'] = $taskJson;
                $task['step_id'] = $step['id'];
                $task['entity_code'] = $entity['code'];
                $task['name'] = $step['name'];
                $task['code'] = $step['code'];
                $task['man_hour'] = $step['man_hour'] ?? 0;
                if (isset($step['enable']) && $step['enable'] === false) {
                    $task['json']['task_deprecate_data'] = $task['json'];
                    $task['json']['task_status'] = $taskCloseStatus['id'];
                }
                $task['json'] = json_encode($task['json']);
                // 检查$edges关系
                $preStepId = 0;
                array_map(function ($item) use (&$step, &$preStepId) {
                    if ($item['target'] == $step['id']) {
                        $preStepId = $item['source'];
                    }
                }, $edges);
                $task['pre_task_id'] = 0;
                if ($preStepId > 0 && isset($stepTaskMap[$preStepId])) {
                    $task['pre_task_id'] = $stepTaskMap[$preStepId]['id'];
                }
                $newTask = $this->copyTask($task, $task['project_id'], $task['tenant_id'], 'sub');
                $stepTaskMap[$step["id"]] = $newTask;
            }
        }
        return array_values($stepTaskMap);
    }


    /**
     * 自动生成集数任务
     * @param int $projectId
     * @return array
     * @throws Throwable
     */
    public function autoGenerateEpisodesTask(int $projectId)
    {
        // 读取集数
        $projectModel = model(ProjectModel::class);
        $projectData = $projectModel->field('tenant_id,episodes')->where(['id' => $projectId])->find();

        if (empty($projectData['episodes'])) {
            throw_strack_exception('Project does not exist', ErrorCode::PROJECT_NOT_EXIST);
        }

        $len = strlen((string)$projectData['episodes']);
        $len = max($len, 3);

        // 查询需要自动生成集数任务的模块
        $episodesTypeStepCategory = model(StepCategoryModel::class)->field('id,name,code,entity_id')
            ->where(['entity_id' => module_by_code('episode')['id'], 'parent_id' => 0])
            ->select();

        if (empty($episodesTypeStepCategory)) {
            return [];
        }

        $resData = [];
        $entityModel = model(EntityModel::class);
        $stepCategoryCodes = array_column($episodesTypeStepCategory, 'code');
        $stepWorkFlows = model(StepWorkflowModel::class)->where(['code' => ["in", $stepCategoryCodes]])->select();
        $stepWorkFlows = array_column($stepWorkFlows, null, 'code');
        // 开启事务处理
        $entityModel->startTrans();
        try {
            foreach ($episodesTypeStepCategory as $episodesTypeStepCategoryItem) {
                // 获取所有集数id
                $entityEpisodeData = $entityModel->field('id,name')
                    ->where([
                        'project_id' => $projectId,
                        'module_id' => $episodesTypeStepCategoryItem['entity_id']
                    ])
                    ->select();

                if (empty($entityEpisodeData)) {
                    // 不存在集数数据则生成，存在忽略
                    for ($i = 1; $i <= $projectData['episodes']; $i++) {

                        $initialEpisodeCode = "EP" . str_pad($i, $len, 0, STR_PAD_LEFT);

                        if ($episodesTypeStepCategoryItem['code'] == "episode") {
                            $name = $initialEpisodeCode;
                            $code = $initialEpisodeCode;
                        } else {
                            $name = "$initialEpisodeCode{$episodesTypeStepCategoryItem['name']}";
                            $code = generate_code($name);
                        }

                        $stepWorkflowId = 0;
                        if (isset($stepWorkFlows[$episodesTypeStepCategoryItem['code']])) {
                            $stepWorkflowId = $stepWorkFlows[$episodesTypeStepCategoryItem['code']]['id'];
                        }
                        $newEntity = [
                            'name' => $name,
                            'code' => $code,
                            'tenant_id' => $projectData['tenant_id'],
                            'project_id' => $projectId,
                            'module_id' => $episodesTypeStepCategoryItem['entity_id'],
                            'category_id' => $episodesTypeStepCategoryItem['id'],
                            'initial_episode_code' => $initialEpisodeCode,
                            'step_workflow_id' => $stepWorkflowId,
                            'uuid' => create_uuid(),
                            'json' => json_encode([
                                $episodesTypeStepCategoryItem['code'] . '_grade' => "A"
                            ])
                        ];
                        $entityModel->add($newEntity);
                    }
                }
            }

            $entityModel->commit();
        } catch (Throwable $e) {
            $entityModel->rollback();
            throw $e;
        }

        return $resData;
    }

    /**
     * 生成子任务
     * @param $taskData
     * @param $data
     * @return mixed
     * @throws \Exception
     */
    public function getDismantleStepCategoryTaskData($taskData, $data)
    {
        unset($taskData['id']);
        $stepData = model(StepModel::class)->field('*')
            ->where(['step_category_id' => $taskData['step_category_id'], 'is_dismantle_step' => 'yes'])
            ->order('id DESC')
            ->find();

        if (empty($stepData)) {
            throw new LogicException('dismantle step step_category not find', ErrorCode::STEP_CATEGORY_NOT_FOUND);
        }
        $statusData = StatusService::getInstance()->getTaskDefaultReadyStatus();
        $dismantleJson = json_decode($taskData['json'], true);
        $dismantleJson['is_dismantle_task'] = true;
        $dismantleJson['parent_step_code'] = $taskData['code'];
        $dismantleJson['parent_task_id'] = $data['task_id'];
        $dismantleJson['task_status'] = $statusData['id']; // 默认未开始
        $dismantleJson['dismantle_name'] = $data['dismantle_name'];
        $dismantleJson['reference_asset'] = !empty($data['reference_asset']) ? $data['reference_asset'] : ['reference_asset' => []];//新增拆分子任务数据
        $taskData['uuid'] = create_uuid();
        $taskData['json'] = json_encode($dismantleJson);
        $taskData['code'] = "SUBTASK_" . generate_asset_code($data['dismantle_name'], 6);
        $taskData['name'] = $stepData['name'];
        $taskData['step_id'] = $stepData['id'];
        $taskData['executor'] = 0;
        $taskData['is_assignee'] = 'no';
        $taskData['assign_status'] = TaskModel::ASSIGN_STATUS_DEFAULT; // 待制片分配
        return $taskData;
    }

    /**
     * 克隆剧集信息
     * @param $sourceEpisodeId
     * @param $projectId
     * @param $tenantId
     * @return bool|float|int|mixed|string
     * @throws \Exception
     */
    private function copyEpisode($sourceEpisodeId, $projectId, $tenantId)
    {
        $sourceEpisode = model(EntityModel::class)->cache(C('database.default_query_cache_time'))->find($sourceEpisodeId);
        if (empty($sourceEpisode)) {
            trace("sourceEpisode not found", "ERR");
            return 0;
        }
        $currentEpisodeFilter = [
            'project_id' => $projectId,
            'tenant_id' => $tenantId,
            'module_id' => Module::$moduleDictData['module_index_by_code']['episode']['id'],
            'code' => $sourceEpisode['code'],
        ];
        $episode = model(EntityModel::class)->where($currentEpisodeFilter)->field('id')->find();
        if (!empty($episode)) {
            return $episode['id'];
        }
        $newEpisode = $sourceEpisode;
        $newEpisode['tenant_id'] = $tenantId;
        $newEpisode['project_id'] = $projectId;
        $newEpisode['uuid'] = create_uuid();
        unset($newEpisode['id']);
        $episodeId = model(EntityModel::class)->add($newEpisode);
        if ($episodeId === false) {
            throw new LogicException("episode clone failure" . model(EntityModel::class)->getError(), ErrorCode::TASK_CLONE_FAILURE);
        }
        return $episodeId;
    }

}
